1 /*
2 * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.security.jca;
27
28 import java.io.File;
29 import java.lang.reflect.*;
30
31 import java.security.*;
32
33 import sun.security.util.PropertyExpander;
34
35 /**
36 * Class representing a configured provider. Encapsulates configuration
37 * (className plus optional argument), the provider loading logic, and
38 * the loaded Provider object itself.
39 *
40 * @author Andreas Sterbenz
41 * @since 1.5
42 */
43 final class ProviderConfig {
44
45 private final static sun.security.util.Debug debug =
46 sun.security.util.Debug.getInstance("jca", "ProviderConfig");
47
48 // classname of the SunPKCS11-Solaris provider
49 private static final String P11_SOL_NAME =
50 "sun.security.pkcs11.SunPKCS11";
51
52 // config file argument of the SunPKCS11-Solaris provider
53 private static final String P11_SOL_ARG =
54 "${java.home}/lib/security/sunpkcs11-solaris.cfg";
55
56 // maximum number of times to try loading a provider before giving up
57 private final static int MAX_LOAD_TRIES = 30;
58
59 // parameters for the Provider(String) constructor,
60 // use by doLoadProvider()
61 private final static Class[] CL_STRING = { String.class };
62
63 // name of the provider class
64 private final String className;
65
66 // argument to the provider constructor,
67 // empty string indicates no-arg constructor
68 private final String argument;
69
70 // number of times we have already tried to load this provider
71 private int tries;
72
73 // Provider object, if loaded
74 private volatile Provider provider;
75
76 // flag indicating if we are currently trying to load the provider
77 // used to detect recursion
78 private boolean isLoading;
79
80 ProviderConfig(String className, String argument) {
81 if (className.equals(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) {
82 checkSunPKCS11Solaris();
83 }
84 this.className = className;
85 this.argument = expand(argument);
86 }
87
88 ProviderConfig(String className) {
89 this(className, "");
90 }
91
92 ProviderConfig(Provider provider) {
93 this.className = provider.getClass().getName();
94 this.argument = "";
95 this.provider = provider;
96 }
97
98 // check if we should try to load the SunPKCS11-Solaris provider
99 // avoid if not available (pre Solaris 10) to reduce startup time
100 // or if disabled via system property
101 private void checkSunPKCS11Solaris() {
102 Boolean o = AccessController.doPrivileged(
103 new PrivilegedAction<Boolean>() {
104 public Boolean run() {
105 File file = new File("/usr/lib/libpkcs11.so");
106 if (file.exists() == false) {
107 return Boolean.FALSE;
108 }
109 if ("false".equalsIgnoreCase(System.getProperty
110 ("sun.security.pkcs11.enable-solaris"))) {
111 return Boolean.FALSE;
112 }
113 return Boolean.TRUE;
114 }
115 });
116 if (o == Boolean.FALSE) {
117 tries = MAX_LOAD_TRIES;
118 }
119 }
120
121 private boolean hasArgument() {
122 return argument.length() != 0;
123 }
124
125 // should we try to load this provider?
126 private boolean shouldLoad() {
127 return (tries < MAX_LOAD_TRIES);
128 }
129
130 // do not try to load this provider again
131 private void disableLoad() {
132 tries = MAX_LOAD_TRIES;
133 }
134
135 boolean isLoaded() {
136 return (provider != null);
137 }
138
139 public boolean equals(Object obj) {
140 if (this == obj) {
141 return true;
142 }
143 if (obj instanceof ProviderConfig == false) {
144 return false;
145 }
146 ProviderConfig other = (ProviderConfig)obj;
147 return this.className.equals(other.className)
148 && this.argument.equals(other.argument);
149 }
150
151 public int hashCode() {
152 return className.hashCode() + argument.hashCode();
153 }
154
155 public String toString() {
156 if (hasArgument()) {
157 return className + "('" + argument + "')";
158 } else {
159 return className;
160 }
161 }
162
163 /**
164 * Get the provider object. Loads the provider if it is not already loaded.
165 */
166 synchronized Provider getProvider() {
167 // volatile variable load
168 Provider p = provider;
169 if (p != null) {
170 return p;
171 }
172 if (shouldLoad() == false) {
173 return null;
174 }
175 if (isLoading) {
176 // because this method is synchronized, this can only
177 // happen if there is recursion.
178 if (debug != null) {
179 debug.println("Recursion loading provider: " + this);
180 new Exception("Call trace").printStackTrace();
181 }
182 return null;
183 }
184 try {
185 isLoading = true;
186 tries++;
187 p = doLoadProvider();
188 } finally {
189 isLoading = false;
190 }
191 provider = p;
192 return p;
193 }
194
195 /**
196 * Load and instantiate the Provider described by this class.
197 *
198 * NOTE use of doPrivileged().
199 *
200 * @return null if the Provider could not be loaded
201 *
202 * @throws ProviderException if executing the Provider's constructor
203 * throws a ProviderException. All other Exceptions are ignored.
204 */
205 private Provider doLoadProvider() {
206 return AccessController.doPrivileged(new PrivilegedAction<Provider>() {
207 public Provider run() {
208 if (debug != null) {
209 debug.println("Loading provider: " + ProviderConfig.this);
210 }
211 try {
212 ClassLoader cl = ClassLoader.getSystemClassLoader();
213 Class<?> provClass;
214 if (cl != null) {
215 provClass = cl.loadClass(className);
216 } else {
217 provClass = Class.forName(className);
218 }
219 Object obj;
220 if (hasArgument() == false) {
221 obj = provClass.newInstance();
222 } else {
223 Constructor<?> cons = provClass.getConstructor(CL_STRING);
224 obj = cons.newInstance(argument);
225 }
226 if (obj instanceof Provider) {
227 if (debug != null) {
228 debug.println("Loaded provider " + obj);
229 }
230 return (Provider)obj;
231 } else {
232 if (debug != null) {
233 debug.println(className + " is not a provider");
234 }
235 disableLoad();
236 return null;
237 }
238 } catch (Exception e) {
239 Throwable t;
240 if (e instanceof InvocationTargetException) {
241 t = ((InvocationTargetException)e).getCause();
242 } else {
243 t = e;
244 }
245 if (debug != null) {
246 debug.println("Error loading provider " + ProviderConfig.this);
247 t.printStackTrace();
248 }
249 // provider indicates fatal error, pass through exception
250 if (t instanceof ProviderException) {
251 throw (ProviderException)t;
252 }
253 // provider indicates that loading should not be retried
254 if (t instanceof UnsupportedOperationException) {
255 disableLoad();
256 }
257 return null;
258 }
259 }
260 });
261 }
262
263 /**
264 * Perform property expansion of the provider value.
265 *
266 * NOTE use of doPrivileged().
267 */
268 private static String expand(final String value) {
269 // shortcut if value does not contain any properties
270 if (value.contains("${") == false) {
271 return value;
272 }
273 return AccessController.doPrivileged(new PrivilegedAction<String>() {
274 public String run() {
275 try {
276 return PropertyExpander.expand(value);
277 } catch (GeneralSecurityException e) {
278 throw new ProviderException(e);
279 }
280 }
281 });
282 }
283
284 }